/**
 * 
 */
package com;

import java.io.*;
import java.net.UnknownHostException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;


/**
 * @author kevin+moaad
 *
 */
public class SDFSClient1 {

	/**
	 * @param args
	 * @throws IOException 
	 */

	public static final String GETDIR = "./Client1Files/GottenFiles/";
	public static final String PUTDIR = "./Client1Files/FilesToPut/";
	public static final String SSLDIR = "./Client1Files/SSLClientKeyStore/";

	public static void main(String[] args) {		
		SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
		SSLSocket clientSocket = null;

		OutputStream outStream =null ;
		PrintWriter outWriter = null;

		InputStream inStream = null;
		BufferedReader inReader = null;
		
		long startTime = 0;
		long finishTime = 0;

		// Connexion to Server through Socket:
		try {
			clientSocket = (SSLSocket) sslsocketfactory.createSocket("localhost", 7785);

			outStream = clientSocket.getOutputStream();
			outWriter = new PrintWriter(outStream, true);

			inStream = clientSocket.getInputStream();
			inReader = new BufferedReader(new InputStreamReader(inStream));

		} catch (UnknownHostException e) {
			System.err.println("*****Don't know about host: localhost.");
			System.exit(1);
		} catch (IOException e) {
			System.err.println("*****Couldn't get I/O for the connection to: localhost.");
			System.exit(1);
		}

		BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));

		String state="START";     

		String fileToPut = null;
		String fileToGet = null;

		while(true){

			if (state.equals("START")) {
				// Receiving welcome message:
				try {
					String serverWelcomeMessage = inReader.readLine();
					if ( serverWelcomeMessage == null ) {
						System.out.println("*****CONNECTION FAILED");
						break;
					} else if (serverWelcomeMessage.equals("WELCOME")) {
						System.out.println("*****Connected to server.");
						state="CONNECTED";
					} else {
						break;
					}
				} catch (IOException e) {
					e.printStackTrace();
				}

			} else if (state.equals("WAITING_FILE")) {

				System.out.println("*****Trying to download file : " + fileToGet);			

				byte[] fileBytes = new byte[1024];
				int numBytesRead;
				try {
					numBytesRead = inStream.read(fileBytes, 0, fileBytes.length);

					String readString = new String(fileBytes, 0, numBytesRead);

					if (readString.equals("!FILE NOT FOUND!")) {
						System.out.println("*****FILE NOT FOUND ON THE SERVER.");
					} else if (readString.equals("!NO ACCESS TO FILE!")){
						System.out.println("*****YOU CANNOT ACCESS TO THIS FILE ON THE SERVER.");					
					} else {
						FileOutputStream fos = new FileOutputStream(GETDIR + fileToGet, false);
						BufferedOutputStream bos = new BufferedOutputStream(fos);

						while (numBytesRead == 1024) {
							bos.write(fileBytes, 0, numBytesRead);
							numBytesRead = inStream.read(fileBytes, 0, fileBytes.length);
						}

						bos.write(fileBytes, 0, numBytesRead);

						bos.close();
						fos.close();

						System.out.println("*****File downloaded at : " + GETDIR + fileToGet);
						finishTime = System.currentTimeMillis();
						System.out.println("*****Getting file took: "+ (finishTime-startTime) + " ms");
					}

				} catch (IOException e) {
					e.printStackTrace();
				}

				state="CONNECTED";

			} else if (state.equals("TRYING_TO_SEND")) {

				File file = new File(PUTDIR + fileToPut);

				if (file.exists()) {
					System.out.println("*****Trying to send file : " + fileToPut);
					outWriter.print("PUT " + fileToPut);
					outWriter.flush();

					byte[] responseBytes = new byte[32];
					int numBytesRead;
					try {
						numBytesRead = inStream.read(responseBytes, 0, responseBytes.length);

						if( (new String(responseBytes, 0, numBytesRead)).equals("!WAITING PUT!")){
							state="SENDING_FILE";
						} else {
							System.out.println("*****SERVER DECLINED TO RECEIVE FILE");
							state="CONNECTED";
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				} else {
					System.out.println("*****File " + PUTDIR + fileToPut + " not found.");
					state="CONNECTED";
				}

			} else if (state.equals("SENDING_FILE")) {

				System.out.println("*****Sending file : " + fileToPut);

				File file = new File(PUTDIR + fileToPut);
				byte[] fileBytes = new byte[(int) file.length()];

				BufferedInputStream bis;
				try {
					bis = new BufferedInputStream(new FileInputStream(file));

					bis.read(fileBytes, 0, fileBytes.length);
					bis.close();

					outStream.write( fileBytes, 0,  fileBytes.length);
					outStream.flush();
				} catch (FileNotFoundException e1) {
					e1.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}

				byte[] responseBytes = new byte[32];
				int numBytesRead;
				try {
					numBytesRead = inStream.read(responseBytes, 0, responseBytes.length);

					if( (new String(responseBytes, 0, numBytesRead)).equals("!PUT FILE SUCCEDED!")){
						System.out.println("*****File successfully sent!");
					} else {
						System.out.println("*****ERROR WHEN SENDING THE FILE :" + fileToPut);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}

				state="CONNECTED";

			} else {

				System.out.println("*****Type your command:");
				String userCommand = null;

				try {
					userCommand = stdIn.readLine();
				} catch (IOException e) {
					e.printStackTrace();
				}

				if (userCommand != null) {
					if (userCommand.length() == 4) {

						if (userCommand.toUpperCase().equals("EXIT")) {
							outWriter.print("EXIT");
							outWriter.flush();
							System.out.println("*****Goodbye!");
							break;

						} else if (userCommand.toUpperCase().equals("HELP")) {
							System.out.println("*****Try GET [FILE_NAME] or PUT [FILE_NAME] or ");
							System.out.println("*****DELE [FILE_NAME] [RECEIVER_NUMBER] [DURATION (ms)] or EXIT.");

						} else {
							System.out.println("*****Command \"" + userCommand + "\" unknown. Type \"HELP\" for help.");
						}

					} else if (userCommand.length() > 4) {

						if (userCommand.substring(0, 4).toUpperCase().equals("GET ")) {

							state="WAITING_FILE";	                	
							fileToGet = userCommand.substring(4);
							
							startTime = System.currentTimeMillis();
							
							outWriter.print("GET " + fileToGet);
							outWriter.flush();

						} else if (userCommand.substring(0, 4).toUpperCase().equals("PUT ")) {

							state="TRYING_TO_SEND";	                	
							fileToPut = userCommand.substring(4);

						} else if (userCommand.substring(0, 5).toUpperCase().equals("DELE ")) {

							String[] delegationCmd = userCommand.split(" ");

							if (delegationCmd.length == 4) {
								boolean formatOK = true;
								try {
									boolean boolean1 = ( getClientID(new Integer(delegationCmd[2])) != 0);
									boolean boolean2 = ( (new Integer(delegationCmd[3])) > 0);
									formatOK = boolean1 && boolean2;
									new Integer(delegationCmd[3]);
								} catch(NumberFormatException e) {
									formatOK = false;
									System.out.println("*****Duration and Receiver number must be integers");
								}
								if (formatOK) {
									int receiverID = getClientID(new Integer(delegationCmd[2]));
									
									outWriter.print("DELE " + delegationCmd[1] +" " + receiverID + " " + delegationCmd[3]);
									outWriter.flush();
									
									try {
										byte[] responseBytes = new byte[32];
										int numBytesRead;
										numBytesRead = inStream.read(responseBytes, 0, responseBytes.length);
										
										if( (new String(responseBytes, 0, numBytesRead)).equals("!DELEGATION SUCCEEDED!")){
											System.out.println("*****Delegation successfully made!");
										} else {
											System.out.println("*****DELEGATION FAILED");
										}
										
									} catch (Exception e) {
										System.out.println("*****DELEGATION FAILED");
									}
								} else {
									System.out.println("*****Duration and Receiver number must be integers");
								}
							} else {
								System.out.println("*****Format is : DELE [FILE_NAME] [RECEIVER_NUMBER] [DURATION (ms)]");
							}

						} else {
							System.out.println("*****Command \"" + userCommand + "\" unknown. Type \"HELP\" for help.");
						}

					} else {
						System.out.println("*****Command \"" + userCommand + "\" unknown. Type \"HELP\" for help.");
					}
				}
			}
		} // fin du while


		try {
			stdIn.close();

			inReader.close();
			inStream.close();

			outWriter.close();
			outStream.close();

			clientSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}	

	private static int getClientID(int clientNumber) {
		String pathToCert = SSLDIR + "clientCA" + clientNumber + ".crt";

		try {
			InputStream is = new FileInputStream(pathToCert);
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
			is.close();
			return cert.getSubjectDN().hashCode();	
		} catch (Exception e) {
			System.out.println("*****The certificate for this client number is not accessible.");
			return 0;
		}
	}
}
